![]() |
Java Database Programming with JDBC
by Pratik Patel Coriolis, The Coriolis Group ISBN: 1576100561 Pub Date: 10/01/96 |
Previous | Table of Contents | Next |
Ive shown you the advantages and disadvantages of implementing an application server; its up to you to weigh these points and other relating factors when it comes time to make a decision on your own system. Lets look at a fully functional application server. The application server shown in Listing 11.1 uses JDBC to interact with data sources, so any JDBC driver could be used. I used the mSQL driver in this example, but you can easily modify the code to use the JDBC-ODBC Bridge, and then use the ODBC drivers for Access 95 to allow applets to query an Access 95 database. (This is an interesting scenario, because Access does not provide direct network connectivity in the form of a true database server.) This application server is truly multithreadedit spawns each client connection into its own thread. Each client connection also make a new instance of the JDBC driver, so each client has its own virtual connection to the data source via the application server.
The application server only allows two real functions:
The query is processed against the data source, and the result is piped directly back to the client in pre-formatted text. You can easily extend this approach so that a ResultSet can be encapsulated and sent unprocessed to the client by using the upcoming remote objects specification from JavaSoft. For the purposes of this example, I wont make it too elaborate and instead just send over the results in a delimited String format. The client is not a true JDBC client in that it does not implement a JDBC driver; it uses the two functions defined earlier to make queries. The results can be parsed by the applet calling the client, but for the purpose of this simple example, well just show them to the user (youll see this when we show the code for the client).
You can find the source file for Listing 11.1 on the CD-ROM or on The Coriolis Groups Web site at http://www.coriolis.com/jdbc-book. Figure 11.2 shows the application servers window.
Figure 11.2 The application server console.
Listing 11.1 Application server.
import java.awt.List; import java.awt.Frame; import java.net.*; import java.io.*; import java.util.*; import java.sql.*; // Remember that we are using the JDBC driver on the _server_ to connect // to a data source, so we need the JDBC API classes! public class ApplicationServer extends Thread { public final static int DEFAULT_PORT = 6001; protected int port; protected ServerSocket server_port; protected ThreadGroup CurrentConnections; protected List connection_list; protected Vector connections; protected ConnectionWatcher watcher; public Frame f; // We plan on showing the connections to the server, so we need a frame // Exit with an error message if there's an exception public static void fail(Exception e, String msg) { System.err.println(msg + ": " + e); System.exit(1); } // Create a ServerSocket to listen for connections and start its thread. public ApplicationServer(int port) { // Create our server thread with a name super("Server"); if (port == 0) port = DEFAULT_PORT; this.port = port; try { server_port = new ServerSocket(port); } catch (IOException e) {fail(e, "Exception creating server socket");} // Create a threadgroup for our connections CurrentConnections = new ThreadGroup("Server Connections"); // Create a window to display our connections in f = new Frame("Server Status"); connection_list = new List(); f.add("Center", connection_list); f.resize(400, 200); f.show(); // Initialize a vector to store our connections in connections = new Vector(); // Create a ConnectionWatcher thread to wait for other threads to die // and to perform clean-up. watcher = new ConnectionWatcher(this); // Start the server listening for connections this.start(); } public void run() { // this is where new connections are listened for try { while(true) { Socket client_socket = server_port.accept(); ServerConnection c = new ServerConnection(client_socket, CurrentConnections, 3, watcher); // Prevent simultaneous access synchronized (connections) { connections.addElement(c); connection_list.addItem(c.getInfo()); } } } catch (IOException e) {fail(e, "Exception while listening for connections");} f.dispose(); System.exit(0); } // Start the server up, get a port number if specified public static void main(String[] args) { int port = 0; if (args.length == 1) { try {port = Integer.parseInt(args[0]);} catch (NumberFormatException e) {port = 0;} } new ApplicationServer(port); } } // This class is the thread that handles all communication with a client. // It also notifies the ConnectionWatcher when the connection is dropped. class ServerConnection extends Thread { static int numberOfConnections = 0; protected Socket client; protected ConnectionWatcher watcher; protected DataInputStream in; protected PrintStream out; Connection con; // Initialize the streams and start the thread public ServerConnection(Socket client_socket, ThreadGroup CurrentConnections, int priority, ConnectionWatcher watcher) { // Give the thread a group, a name, and a priority super(CurrentConnections, "Connection number" + numberOfConnections++); this.setPriority(priority); // We'll need this data later, so store it in local objects client = client_socket; this.watcher = watcher; // Create the streams for talking with client try { in = new DataInputStream(client.getInputStream()); out = new PrintStream(client.getOutputStream()); } catch (IOException e) { try {client.close();} catch (IOException e2) { System.err.println("Exception while getting socket streams: " + e); return;} } // And start the thread up this.start(); } // This is where the real "functionality" of the server takes place. // This is where the input and output is done to the client. public void run() { String inline; try { // Loop forever, or until the connection is broken! while(true) { // Read in a line inline = in.readLine(); if (inline == null) break; // If the client has broken connection, get out of // the loop inline=inline.trim(); // Get rid of leading and trailing whitespace // These are the two functions implemented, connect // and query. The client sends one of these commands, // and if it's query ("S") then the server expects the // next line sent to be the query. switch(inline.toCharArray()[0]) { case `L': out.println("Connected to datasource"); out.println("DONE"); ConnectToDatasource("jdbc:msql://elanor:1112/bcancer", "prpatel"); // See this method next... it starts up the driver and // connects to the data source. break; case `S': out.println("Run query: send SQL Query"); out.println("DONE"); inline = in.readLine(); inline=inline.trim(); // This line gets the query sent here, runs its against // the connected data source, and returns the results in // formatted text out.print(RunQuery(inline)); // RunQuery is the method that runs the passed in // query using the initialized driver and connection. out.println("DONE"); break; default: out.println("ERROR - Invalid Request"); out.println("DONE"); } out.flush(); } } catch (IOException e) {} // If the client broke off the connection, notify the // ConnectionWatcher // (watcher) which will close the connection. finally { try {client.close();} catch (IOException e2) { synchronized (watcher) {watcher.notify();} } } } // This sends info back to the connection starter so that it can // be displayed in the frame. public String getInfo() { return ("Client connected from:"+client.getInetAddress(). getHostName()); } // DB specific stuff follows private void ConnectToDatasource(String url, String Name) { try { new imaginary.sql.iMsqlDriver(); con = DriverManager.getConnection(url, Name, ""); // Create an instance of the driver and connect to the DB server } catch( Exception e ) { e.printStackTrace(); System.out.println(e.getMessage()); } } private String RunQuery(String QueryLine) { // Run the passed in query and return the Stringified results String Output=""; int columns; int pos; try { Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery(QueryLine); columns=(rs.getMetaData()).getColumnCount(); while(rs.next()) { for( pos=1; pos<=columns; pos++) { Output+=rs.getObject(pos)+" "; } Output+="\n"; } stmt.close(); // con.close(); } catch( Exception e ) { e.printStackTrace(); Output=e.getMessage(); } return Output; } // End DB specific stuff } // End class Connection // This class cleans up closed connections and updates the displayed // list of connected clients. class ConnectionWatcher extends Thread { protected ApplicationServer server; protected ConnectionWatcher(ApplicationServer s) { super(s.CurrentConnections, "ConnectionWatcher"); server = s; this.start(); } public synchronized void run() { while(true) { try {this.wait(10000);} catch (InterruptedException e){ System.out.println("Caught an Interrupted Exception"); } // Prevent simultaneous access synchronized(server.connections) { // Loop through the connections for(int i = 0; i < server.connections.size(); i++) { ServerConnection c; c = (ServerConnection)server.connections.elementAt(i); // If the connection thread isn't alive anymore, // remove it from the Vector and List. if (!c.isAlive()) { server.connections.removeElementAt(i); server.connection_list.delItem(i); i--; } } } } } }
Previous | Table of Contents | Next |